package com.myjob.filter;

import com.alibaba.fastjson.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.api.R;
import com.myjob.entity.*;
import com.myjob.enums.RedisEnums;
import com.myjob.service.*;
import com.myjob.utils.RedisUtil;
import com.myjob.utils.UtilValidate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author Chen Yuan
 * @create 2021-03-20  10:11
 */
@Component
@Slf4j
public class TokenAuthenticationFilter extends OncePerRequestFilter {
	@Autowired
	private RoleService roleService;
	@Autowired
	private ResourceService resourceService;
	@Autowired
	private RoleResourceRelService roleResourceRelService;
	@Autowired
	private RedisUtil redisUtil;

	@Override
	protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
		//解析出头中的token
		String token = httpServletRequest.getHeader("user");
		String logout = httpServletRequest.getHeader("logout");
		if(token!=null &&
				(Objects.equals(logout, "") || logout == null)){
			token = URLDecoder.decode(token, "UTF-8");
			//将 token 转成 SecurityUser 对象
			JSONObject userObject = JSONObject.parseObject(token);
			User user = new User();
			user.setUserId(String.valueOf(userObject.get("id")));
			user.setUserName((String) userObject.get("user_name"));
			user.setAccountStatus(String.valueOf(userObject.get("accountStatus")));
			//获取该用户的角色
			Object authorities = userObject.get("authorities");
			List userIds = JSONArray.parseArray(String.valueOf(authorities));
			String roleId = String.valueOf(userIds.get(0));
			roleId = roleId.replace("[", "").replace("]","");
			String roleName = this.getRoleName(Long.parseLong(roleId));
			user.setRole(roleName);

			// 获取该角色对应的所有权限
			List<String> authority = getRoleIdAndResourceCode(Long.parseLong(roleId));
			String authorityString = authority.toString()
												.replace("[","")
												.replace("]","");
			log.info("---- "+ String.valueOf(authority));

			//将用户信息和权限填充 到用户身份token对象中
			UsernamePasswordAuthenticationToken authenticationToken
					= new UsernamePasswordAuthenticationToken(user,null, AuthorityUtils.commaSeparatedStringToAuthorityList(authorityString));
			authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));

			//将authenticationToken填充到安全上下文
			SecurityContextHolder.getContext().setAuthentication(authenticationToken);

		}
		if (Objects.equals(logout, "logout")){
			// 登出，清空 Spring Security 上下文
			UsernamePasswordAuthenticationToken authenticationToken
					= new UsernamePasswordAuthenticationToken(new User(),null);
			authenticationToken.setAuthenticated(false);
			authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
			SecurityContextHolder.getContext().setAuthentication(authenticationToken);
		}
		filterChain.doFilter(httpServletRequest,httpServletResponse);

	}



	public String getRoleName(Long roleId){
		Role role = roleService.getById(roleId);
		return role.getAuthority();
	}

	@Async
	public List<String> getRoleIdAndResourceCode(Long roleId){
		List<String> result = new ArrayList<>();

		Object resourceCodes = redisUtil.hget(RedisEnums.KEY_IN_REDIS.AUTH_ROLE_ID_RESOURCE_CODE_CEL.getValue(), String.valueOf(roleId));
		if (UtilValidate.isNotEmpty(resourceCodes)) {
			log.info("<< AUTH >> --- 从 Redis 中获取 (roleId, resourceCode) 关系成功");
			result = (List<String>) resourceCodes;
		}else {
			log.info("<< AUTH >> --- 从 Redis 中获取 (roleId, resourceCode) 关系失败");
			QueryWrapper<RoleResourceRel> wrapper1 = new QueryWrapper<>();
			wrapper1.eq("role_id", roleId);
			List<RoleResourceRel> roleResourceRels = roleResourceRelService.list(wrapper1);
			List<Long> resourcesIds = roleResourceRels.stream().map(RoleResourceRel::getResourceId).collect(Collectors.toList());

			QueryWrapper<Resource> wrapper2 = new QueryWrapper<>();
			wrapper2.in("resource_id", resourcesIds);
			List<Resource> resources = resourceService.list(wrapper2);

			result = resources.stream().map(Resource::getResourceCode).collect(Collectors.toList());
		}
		return result;
	}
}
